// Own includes.
#include <System/String.h>
#include <System/Collections/Generic/List.h>
#include <System/Exception.h>
#include <System/IndexOutOfRangeException.h>
#include <System/Diagnostics/Debug.h>
#include <System/NotImplementedException.h>
#include <System/Collections/Generic/Dictionary.h>

#include <math.h>

// DEBUG.
#include <Debug.h>

using namespace Riccsson::System;

GC_PTR<Collections::Generic::Dictionary<void*, GC_PTR<String>>> String::_global_list_of_static_const_strings;

// Summary:
//     Represents the empty string. This field is read-only.
const String String::Empty("");

REGISTER_CLASS_CPP( String )
{
	_global_list_of_static_const_strings = new Collections::Generic::Dictionary<void*, GC_PTR<String>>();
}

//---------------------------------------------------------------------------------------
const char* String::string_copy( const char* str, const int& length )
{
	char* newStr = new char[length + 1];

	// Loop every chars inclusive the null char at the end.
	for (int i = length; i >= 0 ; i--)
	{
		newStr[i] = str[i];
	}

	return newStr;
}
//---------------------------------------------------------------------------------------
const char* String::string_copy_nullTerminal( const char* str, int& out_length )
{
	int length(0);

	// Loop every chars inclusive the null char at the end.
	int i(-1);
	for(;;)
	{
		if(str[++i] == '\0')
		{
			length = i;
			break;
		}
	}

	if( i == -1 )
	{
		// Something wrong. Check the code.
		Riccsson::System::Diagnostics::Debug::WriteLine("string_copy_nullTerminal got problem...");
		throw;
	}

	out_length = length;
	return string_copy( str, length );
}

//---------------------------------------------------------------------------------------
String::String()
	: internalString( nullptr )
	, PROP3GET_INIT(String, Length)
{
	internalString = string_copy( "", 0 );
	_Length = 0;
}

//---------------------------------------------------------------------------------------
String::String(const char* value)
	: internalString( nullptr )
	, PROP3GET_INIT(String, Length)
{
	internalString = string_copy_nullTerminal( value, _Length );
}

//---------------------------------------------------------------------------------------
String::String(const char c, int count)
	: internalString( nullptr )
	, PROP3GET_INIT(String, Length)
{
	throw NotImplementedException();
}

//---------------------------------------------------------------------------------------
String::String(const char* value, int startIndex, int length)
	: internalString( new char[length + 1] )
	, PROP3GET_INIT(String, Length)
{
	_Length = length;

	char* modified_str( const_cast<char*>( internalString ) );
	for (int i = 0; i < length; i++)
	{
		modified_str[i] = value[startIndex + i];
	}
	modified_str[length] = '\0';
}

//---------------------------------------------------------------------------------------
String::String(const byte* value, int startIndex, int length)
	: internalString( new char[length + 1] )
	, PROP3GET_INIT(String, Length)
{
	_Length = length;

	char* modified_str( const_cast<char*>( internalString ) );
	for (int i = 0; i < length; i++)
	{
		modified_str[i] = (char)value[startIndex + i];
	}
	modified_str[length] = '\0';
}

//--------------------------------------------------------------------------------------
String::String(Riccsson::System::Array<char>* str)
	: internalString( nullptr )
	, PROP3GET_INIT(String, Length)
{
	throw NotImplementedException();
}

//--------------------------------------------------------------------------------------
String::String(Riccsson::System::Array<char>* value, int startIndex, int length)
	: internalString( nullptr )
	, PROP3GET_INIT(String, Length)
{
	throw NotImplementedException();
}

//--------------------------------------------------------------------------------------
PROP3_GET_CPP(String, int, Length)
{
	return _Length;
}

//---------------------------------------------------------------------------------------
String::~String()
{
	// Delete char*
	if(internalString != nullptr)
	{
		// Delete the char*
		delete internalString;
		internalString = nullptr;

		// Set it to -1 just to know if a coruppted string has called destructor.
		_Length = -1;
	}
}

//---------------------------------------------------------------------------------------
char String::operator[](int index) const
{
	if(index >= _Length) throw IndexOutOfRangeException(String("Cannot access indices outside of the size of the String. (") + index + " >= " + _Length + ")");
	return internalString[index];
}

//--------------------------------------------------------------------------------------
String& String::operator+=(const char * other)
{
	// Dont copy an empty string.
	if(other[0] != '\0')
	{

		// Check the length.
		int length = 0;
		while (other[length] != '\0')
		{
			++length;
		}

		char* newString = new char[_Length +  length + 1];

		// Copy the old.
		memcpy(newString, internalString, sizeof(char) * _Length);

		// Delete last char*
		if(internalString != nullptr)
		{
			delete internalString;
			const_cast<char*&>(internalString) = nullptr;
		}

		// Copy the rest.
		for (int i = _Length; (newString[i] = other[i-_Length]) != '\0'; i++);

		const_cast<char*&>(this->internalString) = newString;

		_Length += length;
	}

	return *this;
}

//--------------------------------------------------------------------------------------
String& String::operator+=(const String& other)
{
	// Dont copy an empty string.
	if(other._Length != 0)
	{
		char* newString = new char[_Length +  other._Length + 1];

		// Copy the old.
		memcpy(newString, internalString, sizeof(char) * _Length);

		// Delete last char*
		if(internalString != nullptr)
		{
			delete internalString;
			const_cast<char*&>(internalString) = nullptr;
		}

		// Copy the rest.
		for (int i = _Length; (newString[i] = other.internalString[i-_Length]) != '\0'; i++);

		internalString = newString;

		_Length += other._Length;
	}

	return *this;
}

char* concat(char *s1, char *s2)
{
    size_t len1 = strlen(s1);
    size_t len2 = strlen(s2);
    char *result = (char*)malloc(len1+len2+1);//+1 for the zero-terminator
    //in real code you would check for errors in malloc here
    memcpy(result, s1, len1);
    memcpy(result+len1, s2, len2+1);//+1 to copy the null-terminator
    return result;
}

//--------------------------------------------------------------------------------------
const String String::operator+(double number) const
{

	int integer = (int)number;
	int decimals = (int)((number - (int)number) * 100000000);

	// Integers.
	char str_ints[32];
#pragma warning( push )
#pragma warning( disable : 4996)
	sprintf(str_ints, "%d", integer);
#pragma warning( pop )

	// Decimals.
	char str_decimals[32];
#pragma warning( push )
#pragma warning( disable : 4996)
	sprintf(str_decimals, "%d", decimals);
#pragma warning( pop )
	
	char* s1 = null;
	char* s2 = null;

	// If decaimals has value.
	if(decimals != 0)
	{
		s1 = concat(str_ints, ".");
		s2 = concat(s1, str_decimals);

		String _this(internalString, 0, _Length);
		_this += s2;

		if(s1)
			delete s1;
	
		if(s2)
			delete s2;

		return _this;
	}
	else
	{
		String _this(internalString, 0, _Length);
		_this += str_ints;
		return _this;
	}
}

//--------------------------------------------------------------------------------------
const String String::operator+(const char* other) const
{
	// Check the length.
	int length = 0;
	while (other[length] != '\0')
	{
		++length;
	}

	char* newString = new char[_Length +  length + 1];

	// Copy the old.
	for (int i = 0; (newString[i] = internalString[i]) != '\0'; i++)
	{
		int dummy = 0;
	}
	
	// Delete last char*
	if(internalString != nullptr)
	{
		delete internalString;
		const_cast<char*&>(internalString) = nullptr;
	}

	// Copy the rest.
	for (int i = _Length; (newString[i] = other[i - _Length]) != '\0'; i++)
	{
		int dummy = 0;
	}
	
	String outputString( newString, 0, _Length + length);
	return outputString;
}

//--------------------------------------------------------------------------------------
const String String::operator+(const String& other) const
{
	return *this + other.internalString;
}

//---------------------------------------------------------------------------------------
const String& String::operator=(const char* str)
{
	// Delete last char*
	if(internalString != nullptr)
	{
		delete internalString;
		internalString = nullptr;
	}

	// Copy the new char*.
	internalString = string_copy_nullTerminal( str, _Length );
	return *this;
}

//--------------------------------------------------------------------------------------
const char* String::c_str() const
{
	return internalString;
}

//--------------------------------------------------------------------------------------
GC_PTR<Object> String::Clone()
{
	throw NotImplementedException();
}

//--------------------------------------------------------------------------------------
const String& String::ToString() const
{
	return *this;
}
/*
int String::Compare(String strA, String strB);
int String::Compare(String strA, String strB, bool ignoreCase);
int String::Compare(String strA, String strB, StringComparison comparisonType);
int String::Compare(String strA, String strB, bool ignoreCase, CultureInfo culture);
int String::Compare(String strA, String strB, CultureInfo culture, CompareOptions options);
int String::Compare(String strA, int indexA, String strB, int indexB, int length);
int String::Compare(String strA, int indexA, String strB, int indexB, int length, bool ignoreCase);
int String::Compare(String strA, int indexA, String strB, int indexB, int length, StringComparison comparisonType);
int String::Compare(String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture);
int String::Compare(String strA, int indexA, String strB, int indexB, int length, CultureInfo culture, CompareOptions options);
int String::CompareOrdinal(String strA, String strB);
int String::CompareOrdinal(String strA, int indexA, String strB, int indexB, int length);
*/

//--------------------------------------------------------------------------------------
int String::CompareTo(Object* value)
{
	if(instanceof(value, const String))
	{
		return CompareTo(dynamic_cast<String*>(value));
	}
	throw Exception("String: CompareTo could not compare object, not derivative of String.");
}


bool isLower(char letter){ return letter >= 'a' && letter <= 'z'; }
bool isUpper(char letter){ return letter >= 'A' && letter <= 'Z'; }
bool isNumber(char letter){ return letter >= '0' && letter <= '9'; }

//--------------------------------------------------------------------------------------
int String::CompareChar(char first, char second)
{
	if(first < second) return -1;
	else if(first > second) return 1;
	else return 0;
}

//--------------------------------------------------------------------------------------
int String::CompareTo(String* strB)
{
	for(int i=0; i<_Length; ++i)
	{
		const char* other = strB->c_str();
		if(isNumber(internalString[i]))
		{
			if(isNumber(other[i]))
			{
				int result = CompareChar(internalString[i], other[i]);
				if(result != 0) return result;
				else continue;
			}
			else
			{
				// If this is a number but other is not, number is smaller.
				return -1;
			}
		}
		else if(isUpper(internalString[i]))
		{
			if(isUpper(other[i]))
			{
				int result = CompareChar(internalString[i], other[i]);
				if(result != 0)
					return result;
				else
					continue;
			}
			else if(isNumber(other[i]))
			{
				// Uppercase has higher value than a number
				return 1;
			}
			else
			{
				// If this is an uppercase but other is not a number, uppercase is smaller.
				return -1;
			}
		}
		else if(isLower(internalString[i]))
		{
			if(isUpper(other[i]))
			{
				int result = CompareChar(internalString[i], other[i]);
				if(result != 0) return result;
				else continue;
			}
			else if(isUpper(other[i]) || isNumber(other[i]))
			{
				// Lowercase has higher value than uppercase or a number
				return 1;
			}
			else
			{
				// If this is an lowercase but other is not a number or uppercase, lowercase is smaller.
				return -1;
			}
		}
		else
		{
			if(isLower(other[i]) || isUpper(other[i]) || isNumber(other[i]))
			{
				// Symbols have higher value than any alphanumerical character.
				return 1;
			}
			else
			{
				//Other is also a symbol, so compare it by its ascii value.
				int result = CompareChar(internalString[i], other[i]);
				if(result != 0) return result;
				else continue;
			}
		}
	}
	return 0;
}
/*
String String::Concat(IEnumerable<String> values);
String String::Concat<T>(IEnumerable<T> values);
String String::Concat(object arg0);
String String::Concat(params object[] args);
String String::Concat(params String[] values);
String String::Concat(object arg0, object arg1);
String String::Concat(String str0, String str1);
String String::Concat(object arg0, object arg1, object arg2);
String String::Concat(String str0, String str1, String str2);
String String::Concat(object arg0, object arg1, object arg2, object arg3);
String String::Concat(String str0, String str1, String str2, String str3);
bool String::Contains(String value);
String String::Copy(String str);
void String::CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count);
bool String::EndsWith(String value);
bool String::EndsWith(String value, StringComparison comparisonType);
bool String::EndsWith(String value, bool ignoreCase, CultureInfo culture);
*/

//--------------------------------------------------------------------------------------
bool String::Equals(object* obj)
{
	if(instanceof(obj, const String))
	{
		return Equals(dynamic_cast<String*>(obj));
	}

	throw Exception("String: Equals could not compare object, not derivative of String.");
}

//--------------------------------------------------------------------------------------
bool String::Equals(String* value)
{
	if(_Length != value->Length) return false;

	for(int i=0; i<_Length; ++i)
	{
		if(internalString[i] != value->c_str()[i])
		{
			return false;
		}
	}

	return true;
}

//--------------------------------------------------------------------------------------
bool String::Equals(const String& a, const String& b)
{
	throw NotImplementedException();
}

//--------------------------------------------------------------------------------------
bool String::Equals(const String& value, const StringComparison& comparisonType) const
{
	throw NotImplementedException();
}

//--------------------------------------------------------------------------------------
bool String::Equals(const String& a, const String& b, const StringComparison& comparisonType)
{
	throw NotImplementedException();
}


//String String::Format(String format, object arg0);
//String String::Format(String format, params object[] args);
//String String::Format(IFormatProvider provider, String format, params object[] args);
//String String::Format(String format, object arg0, object arg1);
//String String::Format(String format, object arg0, object arg1, object arg2);
//CharEnumerator String::GetEnumerator();
//int String::GetHashCode();
//TypeCode GetTypeCode();

//--------------------------------------------------------------------------------------
int String::IndexOf(char value)
{
	for (int i = 0; i < _Length; i++)
	{
		if( internalString[i] == value )
		{
			// Found index of.
			return i;
		}
	}

	// If not found.
	return -1;
}

//--------------------------------------------------------------------------------------
int String::IndexOf(String* value)
{
	throw NotImplementedException();
}

//--------------------------------------------------------------------------------------
int String::IndexOf(char value, int startIndex)
{
	if(startIndex < 0 || startIndex > Length)
	{
		throw Exception();
	}

	for (int i = startIndex; i < Length; i++)
	{
		if(internalString[i] == value)
		{
			// Found index of.
			return i;
		}
	}

	// Did not found index of.
	return -1;
}

//--------------------------------------------------------------------------------------
int String::IndexOf(String* value, int startIndex)
{
	throw NotImplementedException();
}

//int String::IndexOf(String value, StringComparison comparisonType);
//int String::IndexOf(char value, int startIndex, int count);
//int String::IndexOf(String value, int startIndex, int count);
//int String::IndexOf(String value, int startIndex, StringComparison comparisonType);
//int String::IndexOf(String value, int startIndex, int count, StringComparison comparisonType);
//int String::IndexOfAny(char[] anyOf);
//int String::IndexOfAny(char[] anyOf, int startIndex);
//int String::IndexOfAny(char[] anyOf, int startIndex, int count);
//String String::Insert(int startIndex, String value);
//String String::Intern(String str);
//String String::IsInterned(String str);
//bool String::IsNormalized();
//bool String::IsNormalized(NormalizationForm normalizationForm);
//bool String::IsNullOrEmpty(String value);
//bool String::IsNullOrWhiteSpace(String value);
//String String::Join(String separator, IEnumerable<String> values);
//String String::Join<T>(String separator, IEnumerable<T> values);
//String String::Join(String separator, params object[] values);
//String String::Join(String separator, params String[] value);
//String String::Join(String separator, String[] value, int startIndex, int count);

//--------------------------------------------------------------------------------------
int String::LastIndexOf(char value)
{
	for (int i = _Length - 1; i > -1; --i)
	{
		if( internalString[i] == value )
		{
			// Found index of.
			return i;
		}
	}

	// If not found.
	return -1;
}

//--------------------------------------------------------------------------------------
int String::LastIndexOf(String* value)
{
	throw NotImplementedException();
}

//--------------------------------------------------------------------------------------
int String::LastIndexOf(char value, int startIndex)
{
	if(startIndex < 0 || startIndex > Length)
	{
		throw Exception();
	}

	for (int i = startIndex; i > -1; i++)
	{
		if(internalString[i] == value)
		{
			return i;
		}
	}

	return -1;
}

//--------------------------------------------------------------------------------------
int String::LastIndexOf(String* value, int startIndex)
{
	throw NotImplementedException();
}

//int String::LastIndexOf(String value, StringComparison comparisonType);
//int String::LastIndexOf(char value, int startIndex, int count);
//int String::LastIndexOf(String value, int startIndex, int count);
//int String::LastIndexOf(String value, int startIndex, StringComparison comparisonType);
//int String::LastIndexOf(String value, int startIndex, int count, StringComparison comparisonType);
//int String::LastIndexOfAny(char[] anyOf);
//int String::LastIndexOfAny(char[] anyOf, int startIndex);
//int String::LastIndexOfAny(char[] anyOf, int startIndex, int count);
//String String::Normalize();
//String String::Normalize(NormalizationForm normalizationForm);
//String String::PadLeft(int totalWidth);
//String String::PadLeft(int totalWidth, char paddingChar);
//String String::PadRight(int totalWidth);
//String String::PadRight(int totalWidth, char paddingChar);
//String String::Remove(int startIndex);
//String String::Remove(int startIndex, int count);

//--------------------------------------------------------------------------------------
GC_PTR<String> String::Replace(char oldChar, char newChar)
{
	GC_PTR<string> str( new string(internalString) );

	char* newString = const_cast<char*>( str->internalString );
	for (int i = _Length - 1; i > -1; --i)
	{
		if(internalString[i] == oldChar)
		{
			newString[i] = newChar;
		}
	}

	return str;
}

//String String::Replace(String oldValue, String newValue);

//--------------------------------------------------------------------------------------
GC_PTR<Array<GC_PTR<String>>> String::Split(char separator) const
{
	//GC_PTR<Array<GC_PTR<String>>> output;
	GC_PTR<Collections::Generic::List<GC_PTR<String>>> list( new Collections::Generic::List<GC_PTR<String>>(0) );

	int start(0);
	int end(0);

	// Loop the string.
	for (int i = 0; i < _Length; i++)
	{
		// Check when the separator is hiting.
		if(internalString[i] == separator)
		{
			end = i;

			GC_PTR<String> work = GC_PTR<String>( new String( internalString, start, end - start ) );
			list->Add( work );

			start = i + 1;
		}
	}
	GC_PTR<String> work = GC_PTR<String>( new String( internalString, start, _Length - start ) );
	list->Add( work );

	GC_PTR<Array<GC_PTR<String>>> output = list->ToArray();

	return output;
}
//String[] String::Split(char[] separator, int count);
//String[] String::Split(char[] separator, StringSplitOptions options);
//String[] String::Split(String[] separator, StringSplitOptions options);
//String[] String::Split(char[] separator, int count, StringSplitOptions options);
//String[] String::Split(String[] separator, int count, StringSplitOptions options);
//bool String::StartsWith(String value);
//bool String::StartsWith(String value, StringComparison comparisonType);
//bool String::StartsWith(String value, bool ignoreCase, CultureInfo culture);

//-------------------------------------------------------------------------------------
GC_PTR<String> String::Substring(int startIndex)
{
	if(startIndex < 0 || startIndex > Length)
	{
		throw Riccsson::System::ArgumentOutOfRangeException("");
	}

	return new String(internalString, startIndex, Length - startIndex);
}

//-------------------------------------------------------------------------------------
GC_PTR<String> String::Substring(int startIndex, int length)
{
	if(startIndex < 0 || startIndex > Length ||
		startIndex + length > Length)
	{
		throw Riccsson::System::ArgumentOutOfRangeException("");
	}

	return new String(internalString, startIndex, length);
}

//char[] String::ToCharArray();
//char[] String::ToCharArray(int startIndex, int length);
//String String::ToLower();
//String String::ToLower(CultureInfo culture);
//String String::ToLowerInvariant();

//--------------------------------------------------------------------------------------
GC_PTR<String> String::ToString()
{
	return GC_PTR<String>( new String( internalString ) );
}
//String String::ToString(IFormatProvider provider);
//String String::ToUpper();
//String String::ToUpper(CultureInfo culture);
//String String::ToUpperInvariant();

//--------------------------------------------------------------------------------------
GC_PTR<String> String::Trim()
{
	int start = 0;
	int end = 0;
	
	// Find first non '\0'.
	for (int i = 0; i < _Length; i++)
	{
		if(internalString[i] != '\0')
		{
			start = i;
			break;
		}
	}
	
	// Find last non '\0'.
	for (int i = _Length - 1; i > -1; i--)
	{
		if(internalString[i] != '\0')
		{
			end = i;
			break;
		}
	}

	return new String(internalString, start, end);
}

//String String::Trim(params char[] trimChars);
//String String::TrimEnd(params char[] trimChars);
//String String::TrimStart(params char[] trimChars);

//STATIC_CPP(String)
//{
//	_global_list_of_static_const_strings = new Collections::Generic::Dictionary<void*, GC_PTR<String>>();
//}

GC_PTR<Riccsson::System::String> STR(const char* static_const_string)
{
	void* ptr = static_cast<void*>( const_cast<char*>(static_const_string) );

	// Check if string already exist.
	if(Riccsson::System::String::_global_list_of_static_const_strings->ContainsKey( ptr ))
	{
		return (**Riccsson::System::String::_global_list_of_static_const_strings)[ptr];
	}

	// No.
	GC_PTR<Riccsson::System::String> output( new Riccsson::System::String(static_const_string) );

	// Add to dictionary.
	Riccsson::System::String::_global_list_of_static_const_strings->Add(ptr, output);

	return output;
}